//
//
// adventure screen handler for the Shrine Object
//
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "adv_shrine.h"

#include <cassert>
#include <string>

#include "adv_object_model.h"
#include "adv_object_strings.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_sounds.h"
#include "ai_value.h"
#include "army.h"
#include "basic_dialog.h"
#include "bitmap_group.h"
#include "bitmap_group_cache.h"
#include "bitmap_group_ptr.h"
#include "bitset_io.h"
#include "counted_ptr.h"
#include "enum_operations.h"
#include "external_string.h"
#include "format_string.h"
#include "hero.h"
#include "hero_class_properties.h"
#include "hire_hero_dialog.h"
#include "mouse_event.h"
#include "music.h"
#include "object_registration.h"
#include "player.h"
#include "popup_window.h"
#include "simple_dialog.h"
#include "random.h"
#include "replace_keywords.h"
#include "sized_int_types.h"
#include "skill.h"
#include "skill_properties.h"
#include "spell_properties.h"
#include "streambuf_operators.h"
#include "town_properties.h"
#include "town_type.h"


// -------------------------------------------------------
// Unnamed namespace
// -------------------------------------------------------

namespace
{

	// -------------------------------------------------------
	// -------------------------------------------------------
	t_register_with_type<t_adv_shrine> k_shrine_registration( k_adv_object_shrine );
	t_object_registration<t_random_adv_shrine>
		 k_random_shrine_registration( k_adv_object_random_shrine );

	// -------------------------------------------------------
	// -------------------------------------------------------
	int const k_current_map_format_version = 2;
	int const k_current_random_map_format_version = 3;

	// -------------------------------------------------------
	// -------------------------------------------------------
	// -------------------------------------------------------
	// -------------------------------------------------------
	std::string get_shrine_model_name( t_town_type alignment, int spell_level )
	{
		assert( alignment >= 0 && alignment < k_town_type_count );
		assert( alignment != k_town_might );
		assert( spell_level >= 1 && spell_level <= 5 );

		std::string result = "shrines.";
		result += k_town_keyword[ alignment ];
		result += ' ';
		result += format_string( "%i", spell_level );

		return result;
	}

	// -------------------------------------------------------
	// -------------------------------------------------------
	t_spell pick_spell( t_town_type			alignment,
						int					spell_level,
						t_spell_set const &	local_spell_mask,
						t_adventure_map&	map )
	{
		assert( alignment >= 0 && alignment < k_town_type_count );
		assert( alignment != k_town_might );
		assert( spell_level >= 1 && spell_level <= 5 );

		t_spell			spell;
		t_spell_set		candidates;
		t_spell_set		all_spells;

		for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
			all_spells[spell] = (get_spell_level( spell ) == spell_level
								 && get_spell_alignment( spell ) == alignment
								 && is_teachable( spell ));

		candidates = all_spells;
		candidates &= map.get_allowed_spells();
		candidates &= local_spell_mask;
		if (candidates.none())
		{
			candidates = all_spells;
			candidates &= map.get_allowed_spells();
			if (candidates.none())
				candidates = all_spells;
		}
		candidates = map.select_spells( candidates );

		int		choice = random( candidates.count() );

		for (spell = t_spell(0); spell < k_spell_count; enum_incr( spell ))
		{
			if (!candidates[spell])
				continue;
			if (choice-- == 0)
				break;
		}
		map.increment_spell_count( spell );
		return spell;
	}

} // Unnamed namespace

// -------------------------------------------------------
// Shrine object constructor
// -------------------------------------------------------
t_adv_shrine::t_adv_shrine( t_town_type	alignment,
							int			spell_level,
							t_spell		spell )
	:	t_stationary_adventure_object( get_shrine_model_name( alignment, spell_level ) ),
		m_pick_spell( spell )
{
	m_spell_mask.set();
	assert( m_pick_spell >= 0 && m_pick_spell < k_spell_count );
	assert( get_spell_level() == spell_level );
	assert( get_alignment() == alignment );
	assert( ::get_spell_level( m_pick_spell ) == spell_level );
	assert( ::get_spell_alignment( m_pick_spell ) == alignment );
}

// -------------------------------------------------------
// Shrine object constructor
// -------------------------------------------------------
t_adv_shrine::t_adv_shrine( std::string const& model_name, 
		                    t_qualified_adv_object_type const& object_type )
	:	t_stationary_adventure_object( model_name )
{
	m_spell_mask.set();
	m_pick_spell = k_spell_none;
}

t_skill_type t_adv_shrine::get_skill_type() const
{
	return t_skill_type( get_minor_subtype()  + k_skill_life_magic );
}

// -------------------------------------------------------
// activate trigger for the Tavern adventure map object
// -------------------------------------------------------
void t_adv_shrine::activate_trigger( t_army* army, t_adv_map_point const& point, 
		                               t_direction direction, t_adventure_frame* frame )
{
	int                  i;
	t_hero*              hero;
	bool				 already_got_it = false;
	std::vector<t_hero*> students;
	
	assert( m_pick_spell >= 0 && m_pick_spell < k_spell_count );

	// loop through all heroes in the army and award the spell if they dont already have it

	t_creature_array& creatures = army->get_creatures();

	for (i = 0; i < t_creature_array::k_size; i++)
	{
		hero = creatures[i].get_hero();
		if (hero == 0 || hero->is_dead() )
			continue;
		if ( hero->in_spellbook( m_pick_spell ) ) 
		{ // hero knows spell?
			already_got_it = true;
			continue;
		}
		// hero doesnt know it, teach him!
		if (hero->learn_spell(m_pick_spell)) // if has skill to learn it
			students.push_back( hero );
	}

	if (army->get_owner_number() >= 0)
		m_visited[army->get_owner_number()] = true;

	// determine what response to print
	if (army->get_owner()->is_computer())
		return;

	// display the appropriate success message for drinking from this fountain
	t_basic_dialog*     dialog;
	std::string         text;
	std::string         name_text;

	dialog = new t_basic_dialog;
	dialog->add_ok_button();

    name_text = get_text("name");
	name_text = replace_text( name_text );
	dialog->set_title( name_text );
	dialog->add_spell( m_pick_spell, true );

	if ( students.size() > 0 ) 
	{
		text = get_text("initial"); //, "%spell_level", get_skill_level() );
		for (i = 0; i < (int)students.size(); i++)
			dialog->add_creature( *students[i], false );
	} 
	else if (already_got_it) 
	{ // heroes in group, non learned it, someone already had it
		// display the appropriate rebuff for this fountain type
		text = get_text( "empty" );
	}
	else 
	{ // display the denied message
		t_skill	skill( get_skill_type(), get_skill_level() );

		text = get_text( "denied" );

		// add a skill icon also
		dialog->add_skill( skill, true );
	}

	text = replace_text( text );
	dialog->set_text( text );
	dialog->open( get_dialog_bottom( point ), true, k_align_bottom );
	t_sound_cache music = get_music_playing();
	stop_music();
	get_dialog_sound( k_dialog_sound_shrine )->play( get_sound_volume() );
	dialog->run_modal();
	play_music( music );
}

// -------------------------------------------------------
// Shrine as it appears on the adventure map
// -------------------------------------------------------
std::string t_adv_shrine::replace_text( std::string text ) const
{
	std::string spell_level_string = format_string( "%i", get_spell_level() );
	t_skill     skill( get_skill_type(), get_skill_level() );

	text = replace_keywords(text, "%skill_name", ::get_name( skill ) );
	text = replace_keywords(text, "%spell_level", spell_level_string);
	text = replace_keywords(text, "%spell_name", get_spell_name(m_pick_spell) );
	text = replace_keywords(text, "%magic_type", get_alignment_name( get_alignment() ) );
	return text;
}

int t_adv_shrine::get_version() const
{
	return 1;
}

// -------------------------------------------------------
// Shrine as it appears on the adventure map
// -------------------------------------------------------
bool t_adv_shrine::write( std::streambuf& stream ) const
{
	put<t_spell>( stream, m_pick_spell );
	m_visited.write( stream );
	return true;
}

// -------------------------------------------------------
// Shrine as it appears on the adventure map
// -------------------------------------------------------
bool t_adv_shrine::read( std::streambuf&						stream, 
						 t_qualified_adv_object_type const&	type,
						 int									version ) 
{
	m_pick_spell = get<t_spell>( stream );
	if (version > 0)
		m_visited.read( stream );
	return true;
}

// -------------------------------------------------------
// Shrine as it appears on the adventure map
// -------------------------------------------------------
bool t_adv_shrine::read_from_map( std::streambuf & buffer, t_progress_handler * )
{
	int version = get< t_uint16 >( buffer );
	if ( version < 0 || version > k_current_map_format_version )
		return false;

	if ( version >= 2 )
		::read( buffer, m_spell_mask );
	else if ( version >= 1 )
		::read_version( buffer, m_spell_mask, 0 );
	else
		m_spell_mask.set();

	return true;
}

// -------------------------------------------------------
// Shrine as it appears on the adventure map
// -------------------------------------------------------
std::string t_adv_shrine::get_balloon_help() const
{
	std::string help_text = get_text( "name" );

	if (m_visited[get_map()->get_player_number()])
	{
		help_text = get_text( "name_visited" );
	}

	return replace_text( help_text );
}

// -------------------------------------------------------
// Shrine as it appears on the adventure map
// -------------------------------------------------------
void t_adv_shrine::initialize( t_adventure_map& map )
{
	t_stationary_adventure_object::initialize( map );

	// Pick spell if not already known. Note that we can't always choose
	// the spell before this because we don't have access to the map's
	// allowed spell list in read_from_map()
	if ( m_pick_spell == k_spell_none )
	{
		m_pick_spell = pick_spell( get_alignment(), get_spell_level(), m_spell_mask,  map );
	}
}

// -------------------------------------------------------
// Shrine as it appears on the adventure map
// -------------------------------------------------------
void t_adv_shrine::right_click( t_mouse_event const& event,
		                        t_adventure_frame* adventure_frame )
{
	std::string			help_text;
	t_adventure_map*	map	= get_map();
	int					player_number = map->get_player_number();
	
	if (get_information_level() >= k_mastery_basic)
		m_visited[player_number] = true;

	if (m_visited[player_number])
	{
		help_text = get_text( "help_visited" );
	}
	else
		help_text = get_text( "help" );

	if (help_text.empty())
		return;

	help_text = replace_text( help_text );

	if (!m_visited[player_number])
	{
		show_popup_text( help_text, event.screen_point );
		return;
	}

	t_window*					  window;
	t_window*					  modal_window = t_window::get_main_window();
	t_screen_point                point = event.screen_point;
	t_counted_ptr<t_basic_dialog> dialog;

	point = modal_window->to_client( point );
	window = new t_popup_window( point, modal_window );
	window->set_cursor( adventure_frame->get_cursor( false ));
	dialog = new t_basic_dialog( window );
	dialog->add_spell( m_pick_spell );
	dialog->set_text( help_text );
	dialog->open( t_screen_point(0,0), modal_window->get_client_rect() - point, 
		          true, k_align_top );
	window->fit_to_parent_rect();
}

float t_adv_shrine::ai_value( t_adventure_ai const& ai, t_creature_array const& army, int move_cost ) const
{
	float total_value = 0.0f;
	if (m_visited[get_player_number(army)])
	{
		float total_value = 0.0f;
		for (int i = 0; i < t_creature_array::k_size; i++)
		{
			t_hero const* hero = army[i].get_const_hero();
			if ( hero && !hero->in_spellbook(m_pick_spell) ) 
				total_value += ai_value_of_spell( hero, m_pick_spell, &army );
		}
	} 
	else
	{
		for (int i = 0; i < t_creature_array::k_size; i++)
		{
			t_hero const* hero = army[i].get_const_hero();
			if (hero == NULL) continue;
		
			total_value += ai_value_of_generic_spell( hero, &army, get_alignment(), get_spell_level() );
		}
	}
	return total_value;
}

// -------------------------------------------------------
// t_random_adv_shrine members
// -------------------------------------------------------

// -------------------------------------------------------
// -------------------------------------------------------
t_random_adv_shrine::t_random_adv_shrine( std::string const &	source )
	:	t_stationary_adventure_object( source ),
		m_alignment( k_town_life )
{
	m_spell_mask.set();
}

// -------------------------------------------------------
// -------------------------------------------------------
bool t_random_adv_shrine::read_from_map(
	std::streambuf &		buffer,
	t_progress_handler * )
{
	int version = get< t_uint16 >( buffer );
	if ( version < 0 || version > k_current_random_map_format_version )
		return false;

	if ( version >= 2 )
		::read( buffer, m_spell_mask );
	else if ( version >= 1 )
		::read_version( buffer, m_spell_mask, 0 );
	else
		m_spell_mask.set();

	if ( version >= 3 )
	{
		if ( !m_linkage_info.read_from_map( buffer ) )
			return false;
	}
	else
	{
		m_linkage_info.reset();
	}

	return true;
}

// -------------------------------------------------------
// -------------------------------------------------------
bool t_random_adv_shrine::preplacement( t_adventure_map& map, int pass )
{
	if (t_stationary_adventure_object::preplacement( map, pass ))
		return true;

	switch( m_linkage_info.get_link_alignment( map, pass ) )
	{
		case t_linkage_data::k_link_alignment_life:
			m_alignment = k_town_life;
			return false; 

		case t_linkage_data::k_link_alignment_order:
			m_alignment = k_town_order;
			return false; 

		case t_linkage_data::k_link_alignment_death:
			m_alignment = k_town_death;
			return false; 

		case t_linkage_data::k_link_alignment_chaos:
			m_alignment = k_town_chaos;
			return false; 

		case t_linkage_data::k_link_alignment_nature:
			m_alignment = k_town_nature;
			return false; 

		case t_linkage_data::k_link_alignment_wait:
			return true; 

		default:
			assert(false);
			// Fall through to:

		case t_linkage_data::k_link_alignment_might:
		case t_linkage_data::k_link_alignment_any:
			m_alignment = random( k_magic_town_type_count );
			return false; 
	}
}

// -------------------------------------------------------
// -------------------------------------------------------
void t_random_adv_shrine::place( t_adventure_map &		map,
						  		 t_adv_map_point const&	point )
{
	t_spell spell = pick_spell( m_alignment, get_spell_level(), m_spell_mask, map );
	t_counted_ptr< t_adv_shrine > new_shrine_ptr( new t_adv_shrine( m_alignment, 
																	get_spell_level(), spell ) );
	new_shrine_ptr->place( map, point );
}
